package org.gap.wizards.creature;

import java.awt.Point;
import java.util.HashMap;
import java.util.Map;

import org.gap.wizards.utils.Persistence;
import org.gap.wizards.views.ViewPlatform;

public abstract class BaseCreature implements Creature {
	private static final String LOCATION = "location";
	private static final String ATTRIBUTES = "attributes";
	private Point point;
	private Map<Creature.Attribute, AttributeValue> attributes;
	private Description type;

	public BaseCreature() {
		this(null);
	}
	
	public BaseCreature(Description type) {
		this.type = type;
		attributes = new HashMap<Creature.Attribute, AttributeValue>();
		point = new Point(0, 0);
	}
	
	@Override
	public void draw(ViewPlatform console) {
		Point location = getLocation();
		console.print(location.x, location.y, type.getColor(), type.getSymbol());
	}
	
	@Override
	public int getX() {
		return getLocation().x;
	}
	
	@Override
	public int getY() {
		return getLocation().y;
	}

	@Override
	public void setLocation(Point point) {
		this.point = point;
	}

	@Override
	public Point getLocation() {
		return (Point) point.clone();
	}

	/**
	 * Defines an attribute with the maximum, and the current level, such as:
	 * initAttribute(Hitpoints, 10, 5) where 10 is the maximum amount of
	 * hitpoints this player can have, but currently they have only half that
	 * amount (5).
	 * 
	 * @param attr
	 *            the attribute to initialize
	 * @param maximum
	 *            the maximum amount possible
	 * @param currentLevel
	 *            the actual amount the player has
	 */
	@Override
	public void initAttribute(Attribute attr, int maximum, int currentLevel) {
		attributes.put(attr, new AttributeValue(maximum, currentLevel));
	}

	@Override
	public void increase(Attribute attr, int increaseBy) {
		attributes.get(attr).increaseScore(increaseBy);
	}

	@Override
	public void decrease(Attribute attr, int decreaseBy) {
		attributes.get(attr).decreaseScore(decreaseBy);
	}

	@Override
	public int getAttribute(Attribute attr) {
		return attributes.get(attr).currentLevel;
	}

	@Override
	public int getMaxAttribute(Attribute attr) {
		return attributes.get(attr).maxValue;
	}
	
	@Override
	public String toString() {
		return type.getName();
	}

	public void persist(Persistence persister) {
		String[] allAttributes = new String[attributes.size()];
		int i = 0;
		for (Attribute each : attributes.keySet()) {
			allAttributes[i++] = each.name();
			persister.beginSection(each.name());
			{
				AttributeValue value = attributes.get(each);
				persister.set("max", value.maxValue);
				persister.set("current", value.currentLevel);
			}
			persister.endSection();
		}
		persister.set(ATTRIBUTES, allAttributes);
		persister.set(LOCATION, point);
		type.persist(persister);
	}

	public void load(Persistence persister) {
		String[] allAttributes = persister.getStrings(ATTRIBUTES);
		for (String each : allAttributes) {
			persister.beginSection(each);
			{
				int maxValue = persister.getInt("max");
				int currentValue = persister.getInt("current");
				
				Attribute attribute = Attribute.valueOf(each);
				AttributeValue value = new AttributeValue(maxValue, currentValue);
				attributes.put(attribute, value);
			}
			persister.endSection();
		}
		point = persister.getPoint(LOCATION);
		type = new Description();
		type.load(persister);
	}

	public boolean equals(BaseCreature obj) {
		if (!point.equals(obj.point)) {
			return false;
		}
		if (attributes.size() != obj.attributes.size()) {
			return false;
		}
		for (Attribute each : attributes.keySet()) {
			if (!obj.attributes.containsKey(each)) {
				return false;
			}
			if (!attributes.get(each).equals(obj.attributes.get(each))) {
				return false;
			}
		}
		return type.equals(obj.type);
	}

	private class AttributeValue {
		int maxValue;
		int currentLevel;

		public AttributeValue(int maxValue, int currentLevel) {
			this.maxValue = maxValue;
			this.currentLevel = currentLevel;
		}

		public void increaseScore(int amount) {
			currentLevel += amount;
			if (currentLevel > maxValue) {
				currentLevel = maxValue;
			}
		}

		public void decreaseScore(int amount) {
			currentLevel -= amount;
		}

		@Override
		public boolean equals(Object obj) {
			if (obj == null) {
				return false;
			}
			if (!obj.getClass().equals(AttributeValue.class)) {
				return false;
			}
			AttributeValue other = (AttributeValue) obj;
			return maxValue == other.maxValue && currentLevel == other.currentLevel;
		}
	}

}
